import { _decorator, tween, Animation, AnimationState, CCString, Collider2D, Component, Contact2DType, director, Enum, IPhysics2DContact, Node, PhysicsSystem2D, UITransform, view, AudioClip, Collider } from 'cc';
import { Bullet } from './Bullet';
import { L } from './tools/L';
import { AudioManager } from './tools/AudioManager';
const { ccclass, property } = _decorator;

export enum Direction {
    UP = "UP", DOWN = "DOWN", FIXED = "FIXED"
}

export enum Role {
    BULLET = "BULLET", ENEMY = "ENEMY", PLAYER = "PLAYER", REWARD = "REWARD", UNKNOWN = "UNKNOWN"
}

Enum(Direction);
Enum(Role);

const TAG = 'MoveBaseEntity';
/**
 * 移动实体基类
 */
@ccclass('MoveBaseEntity')
export class MoveBaseEntity extends Component {
    @property({ type: Direction })
    direction: Direction;
    @property({ type: Role })
    role: Role;

    /**移动速度 */
    @property
    speed: number = 300;

    @property(Animation)
    animation:Animation;

    /**击毁动画 */
    @property(CCString)
    downAnimation: string = "";
    @property(AudioClip)
    downAudio: AudioClip | null = null;
    @property(AudioClip)
    createAudio: AudioClip | null = null;

    // /**击中动画 */
    @property(CCString)
    hitAnimation: string = "";

    /**血量值 */
    @property
    hp: number = 1;
    
    /**
     * 撞后的无敌时间(秒), 及不会产生碰撞
     */
    @property 
    invincibleTime: number = 0; 
    protected _isInvincible: boolean = false; 
    protected _isDead: boolean = false;

    attr() : string {
        return `节点名称:${this.node.name}, uuid: ${this.node.uuid}, role: ${this.role}, 当前血量: ${this.hp}, isDead: ${this._isDead}, speed: ${this.speed}, animation: ${this.animation}, downAnimation: ${this.downAnimation}, hitAnimation: ${this.hitAnimation}`;
    }

    start() {
        let collider = this.getComponent(Collider2D);
        if (collider) {
            collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
            L.debug(this.role, `${this.role}[${this.node.name}] 注册了碰撞事件`);
        }

        if(this.createAudio) {
            AudioManager.inst.playOneShot(this.createAudio);
        }
    }

    update(dt: number): void {
        // 检查节点基础状态
        if (!this.node || !this.node.isValid) {
            L.warn(this.role, '节点已被销毁或无效');
            return;
        }

        // 检查节点可见性
        if (!this.node.activeInHierarchy) {
            L.warn(this.role, '节点在层级中未激活');
            return;
        }

        if(this.direction == Direction.FIXED) {
            //FIXED 不需要对于判断是否超出屏幕, 因为不会自动移动
        } else {
            // 获取视口高度（世界坐标系）
            const visibleHeight = view.getVisibleSize().height;
            const contentSize = this.node.getComponent(UITransform).contentSize;

            const worldPos = this.node.getWorldPosition();
            if (this.direction == Direction.UP) {
                const maxY = visibleHeight + contentSize.height * 0.5;
                if (worldPos.y >= maxY) {
                    L.debug(this.role, `${this.node.name} 世界坐标: [${worldPos}]超过屏幕高度[顶部] ${maxY} 将销毁`);
                    this.node.destroy();
                }
            } else if (this.direction == Direction.DOWN) {
                const minY = -contentSize.height * 0.5;
                if (worldPos.y <= minY) {
                    L.debug(this.role, `${this.node.name} 世界坐标: [${worldPos}]超过屏幕高度[底部] ${minY} 将销毁`);
                    this.node.destroy();
                }
            }
        }
    }
    /**
     * 碰撞前的检测, 如果返回false, 则不会执行碰撞逻辑, 参数详见 `onBeginContact` 方法
     * @param selfCollider 
     * @param otherCollider 
     * @param contact 
     * @returns `true` - 继续执行碰撞逻辑, `false` - 不执行碰撞
     */
    protected checkPreBeginContact (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) : boolean {
        return true;
    }

    /**
     * 当血量有改变
     */
    protected hpChanged(): void {
        
    }

    onBeginContact (selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        L.debug(this.role,
            `💥 💥 💥  ${this.role} - ${selfCollider.node.name} 碰撞了 ${otherCollider.node.name}！\n` + 
            `[节点信息] ${this.attr()} \n` +
            `  SELF:  type=${selfCollider.constructor.name} node=${selfCollider.node.name}  group=${selfCollider.group}  sensor=${selfCollider.sensor}\n` +
            `  FRAME: ${director.getTotalFrames()}\n` +
            `  OTHER: type=${otherCollider.constructor.name}  node=${otherCollider.node.name}   group=${otherCollider.group}  sensor=${otherCollider.sensor}\n` +
            `  POS:   self=${selfCollider.node.worldPosition}  other=${otherCollider.node.worldPosition}\n` +
            `----------------------------------`
        );

        if(! this.checkPreBeginContact(selfCollider, otherCollider, contact)) {
            L.debug(this.role, `checkPreBeginContact 返回 false, 不执行碰撞逻辑`);
            return;
        }

        if(this._isInvincible) {
            L.debug(this.role, this.role, `[无敌] ${this.role} 碰撞时处于无敌中, invincibleTime: ${this.invincibleTime}`);
            return;
        }

        if(this.invincibleTime) {
            L.debug(this.role, this.role, `[无敌] 被碰撞后无敌时间开启`);
            this._isInvincible = true;
            this.scheduleOnce(() => {
                L.debug(this.role, `[无敌] 超过无敌时间 : ${this.invincibleTime}, 无敌关闭`);
                this._isInvincible = false;
            }, this.invincibleTime);
        }

        const dt = this._injured(1);
        if(this.hp <= 0) {
            this.dead(dt);
        }
    }

    protected onDestroy(): void {
        L.debug(this.role, `[onDestroy] ${this.role} [${this.node.name}] 被销毁`)
        this.unscheduleAllCallbacks();
        let collider = this.getComponent(Collider2D);
        if(collider) {
            collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
        }
    }

    /**
     * 受伤逻辑
     * 
     * 1,血量减少
     * 2,播放对应的动画
     * 3,播放音效
     * @param reduceHp 需要减少的血量
     * @returns 返回需要延迟调用的时间, 有时候dead的时候需要等待动画播放完成, 如果没有配置死亡动画, 则默认返回 1/1000 秒
     */
    _injured(reduceHp: number): number {
        this.hp -= reduceHp;
        this.hpChanged();
        const animationState = this._checkAndPlayAnimation();
        if(animationState) {
            const durationSeconds = animationState.duration / animationState.speed;
            return durationSeconds;
        }

        return 1 / 1000;
    }
    
    _checkAndPlayAnimation() : AnimationState {
        if(! this.animation) return null;
        this.animation.stop(); 
        let play = this.hp > 0 ? this.hitAnimation : this.downAnimation;
        this.animation.play(play);
        // 添加事件监听验证
        this.animation.on(Animation.EventType.PLAY, () => {
            L.debug(this.role, `[动画事件] 开始播放: ${play}`);
        }, this);
        
        this.animation.on(Animation.EventType.FINISHED, () => {
            L.debug(this.role, `[动画事件] 播放完成: ${play}`);
            if(this.animation.playOnLoad) {
                L.debug(this.role, `[动画事件] 有加载后播放, 则播放默认: ${this.animation.defaultClip?.name || '空' }`);
                this.animation.play();
            }
        }, this);
        
        L.debug(this.role, `[动画调试] 
            节点: ${this.node.name}
            动画组件: ${this.animation.constructor.name}
            当前动画: ${this.animation.defaultClip?.name || '无'}
            待播放动画: ${this.hitAnimation}
            动画列表: ${this.animation.clips.map(c => c.name).join(',')}
            `);
            
        L.debug(this.role, `[深度调试] 
            当前权重: ${this.animation.getState(this.hitAnimation).weight}
            是否播放中: ${this.animation.getState(this.hitAnimation).isPlaying}
            播放速度: ${this.animation.getState(this.hitAnimation).speed}
            循环模式: ${this.animation.getState(this.hitAnimation).wrapMode}
            `);

        L.debug(this.role, `播放动画 [节点信息] ${this.attr()} \n`);
        const animationState = this.animation.getState(play);
        return animationState;
    }

    dead(dt: number): boolean {
        L.debug(this.role, 
            `💥 处理[${this.role}] DEAD 逻辑！\n` + 
            `[节点信息] ${this.attr()} \n` +
            `----------------------------------`
        );

        if(this._isDead) return false;
        
        if(this.downAudio) {
            AudioManager.inst.playOneShot(this.downAudio);
        }
        
        let collider = this.getComponent(Collider2D);
        if(collider) {
            collider.enabled = false;
        }

        this.scheduleOnce(() => { this.node.destroy() }, dt);
        this._isDead = true;
        return true;
    }
}

